package router

import (
	"fmt"
	"gitee.com/baizhige/gin-common-handler/ctxconst"
	"gitee.com/baizhige/go-server-tempalte/common/auth"
	"gitee.com/baizhige/go-server-tempalte/common/constant"
	"gitee.com/baizhige/go-server-tempalte/common/errs"
	"gitee.com/baizhige/go-server-tempalte/common/log"
	"gitee.com/baizhige/go-server-tempalte/common/response"
	"github.com/gin-gonic/gin"
	"net/http"
	"runtime"
	"time"
)

// CommonMiddleware 通用中间件
func CommonMiddleware[T auth.User](c *gin.Context) {
	// 生成请求唯一标志
	traceID := fmt.Sprint(time.Now().Nanosecond())
	url := c.Request.URL.Path
	log.InfoTF(traceID, "Req Path Is : %s", url)
	c.Set(constant.ContextTraceID, traceID)

	// 设置语言
	setLang(c)

	//可能会404路由没找到
	router, err := setRouter(traceID, c)
	if err != nil {
		panic(err)
	}
	if !router.Enable {
		//若未启用 记录日志
		// 请求前置日志
		err = reqPreLog[T](c, router, traceID)
		if err != nil {
			panic(err)
		}
		panic(constant.ErrRouterEnable)
	}

	//401 尚未登陆 如果登录了 将会把用户登录信息放到上下文中
	//user, err := auth.SetUser[model_cache.CacheAccount](traceID, c)
	_, err = auth.SetUser[T](traceID, c)
	if err != nil && router.Login() {
		panic(err)
	}

	//  403 授权异常（无权限），
	err = auth.Authentication[T](traceID, c, router)
	if err != nil {
		log.ErrorTF(traceID, "【authorization】err:%v", err)
		panic(err)
	}

	// 签名验证（如有
	err = signValid(traceID, c, router)
	if err != nil {
		panic(err)
	}

	// 数据解密(如有
	err = reqBodyDec(traceID, c, router)
	if err != nil {
		panic(err)
	}

	// 请求前置日志
	err = reqPreLog[T](c, router, traceID)
	if err != nil {
		panic(err)
	}

	//执行请求
	c.Next()

	// 请求后置日志
	err = respLog(traceID, router, c)
	if err != nil {
		panic(err)
	}
	// 响应数据加密
	err = respEnc(traceID, c, router)
	if err != nil {
		panic(err)
	}
}

// ResultReceiveMiddleware 结果统一包装，异常捕获统一处理
func ResultReceiveMiddleware(ctx *gin.Context) {
	language := ctx.GetHeader(HeaderLanguage)
	platType := ctx.GetHeader(HeaderPlatType) //平台类型
	if ctx.FullPath() == "" {
		ctx.JSON(http.StatusOK, constant.ErrSystem.ToResponse(platType, language))
		ctx.Abort()
		return
	}

	defer func() {
		traceId := ctx.GetString(constant.ContextTraceID)
		router, _ := ctx.Get(constant.ContextRouter)

		a := recover()
		if a == nil { //没有捕捉到东西 则结束
			return
		}
		//异常统一捕获
		appErr, ok := a.(errs.Error)
		if !ok {
			//说明不是框架的错误， 需要记录起来打印panic,并且系统错误
			buf := make([]byte, 1024)
			n := runtime.Stack(buf, false)            // 获取堆栈信息
			fmt.Printf("Stack trace:\n%s\n", buf[:n]) //TODO

			res := response.SystemErrResponse.ToRes(language)
			// 请求后置日志
			err := recordLog(traceId, router.(*Router), ctx, res)
			if err != nil {
				log.ErrorTF(traceId, "resp log error:%v", err)
			}
			ctx.JSON(http.StatusOK, res)
			ctx.Abort()
			return
		}

		//框架错误类型可以直接转换返回
		toResponse := appErr.ToResponse(platType, language)
		// 请求后置日志
		err := recordLog(traceId, router.(*Router), ctx, toResponse)
		if err != nil {
			log.ErrorTF(traceId, "resp log error:%v", err)
		}
		ctx.JSON(http.StatusOK, toResponse)
		ctx.Abort()
	}()

	// 处理请求
	ctx.Next()

	// 响应统一封装
	value, exists := ctx.Get(ctxconst.FuncResult) //拿到运行结果
	if !exists {
		ctx.JSON(http.StatusOK, response.Success(language, nil))
		return
	}
	funcResults := value.([]interface{})
	if len(funcResults) == 0 {
		ctx.JSON(http.StatusOK, response.Success(language, nil))
		return
	}
	ctx.JSON(http.StatusOK, response.Success(language, funcResults[0]))
}

// setLang 设置语言上下文
func setLang(c *gin.Context) {
	lang := c.GetHeader(constant.HeaderLang)
	if lang == "" || lang == "null" {
		lang = "zh-CN"
	}
	c.Set(constant.ContextLang, lang)
}

// setRouter 设置路由
func setRouter(traceId string, c *gin.Context) (apiInfo *Router, err error) {
	apiInfo, ok := Get(c.Request.Method, c.FullPath())
	if !ok {
		panic(constant.ErrPageNotFind)
	}
	c.Set(constant.ContextRouter, apiInfo)
	return
}

func GetResponseEntity(traceId string, c *gin.Context) (response interface{}, arr []interface{}, err error) {
	value, exists := c.Get(ctxconst.FuncResult) //结果为空时
	if !exists {
		return
	}
	arr, ok := value.([]interface{})
	if !ok {
		log.ErrorTF(traceId, "get resp value error")
		err = constant.ErrContractor
		return
	}
	if len(arr) == 0 || arr[0] == nil { //结果为空
		return
	}

	response = arr[0]
	return
}
